namespace SimpleWebServices.Web.UI.Html
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Xml;
    using System.Xml.Serialization;
    using System.Xml.XPath;
    using SimpleWebServices;
    using SimpleWebServices.Web.UI.Html.Elements;
    using Xunit;

    public sealed class ElementCollectionFacts
    {
        [Fact]
        public void typedef()
        {
            Assert.True(new ObjectExpectations<ElementCollection>()
                .DerivesFrom<object>()
                .IsConcreteClass()
                .IsSealed()
                .HasDefaultConstructor()
                .XmlRoot("elements")
                .Implements<IList<IElementSerializable>>()
                .Implements<IXmlSerializable>()
                .Result);
        }

        [Fact]
        public void deserialize()
        {
            ElementCollection actual = (
                "<elements>" +
                    "<div id='abc'></div>" +
                    "<div id='xyz' />" +
                "</elements>").Deserialize<ElementCollection>();

            Assert.Equal<int>(2, actual.Count);

            Assert.Equal<Name>("abc", actual[0].ToElement().Id);
            Assert.Equal<Name>("xyz", actual[1].ToElement().Id);
        }

        [Fact]
        public void deserializeBody()
        {
            ElementCollection actual = (
                "<elements>" +
                    "<body>" +
                        "<div>" +
                            "<div id='div'>" +
                            "</div>" +
                        "</div>" +
                        "<dl id='dl'>" +
                            "<dt id='dt'>definition term text</dt>" +
                            "<dd id='dd'>definition  description text</dd>" +
                        "</dl>" +
                        "<form id='form'>" +
                            "<fieldset id='fieldset'>" +
                                "<legend id='legend'>legend text</legend>" +
                            "</fieldset>" +
                            "<button id='button' type='button' />" +
                            "<button id='button-reset' type='reset' />" +
                            "<button id='button-submit' type='submit' />" +
                            "<label id='label'>label text</label>" +
                            "<input id='input-button' type='button' />" +
                            "<input id='input-checkbox' type='checkbox' />" +
                            "<input id='input-file' type='file' />" +
                            "<input id='input-hidden' type='hidden' />" +
                            "<input id='input-image' type='image' />" +
                            "<input id='input-password' type='password' />" +
                            "<input id='input-radio' type='radio' />" +
                            "<input id='input-reset' type='reset' />" +
                            "<input id='input-submit' type='submit' />" +
                            "<input id='input-text' type='text' />" +
                            "<select id='select'>" +
                                "<optgroup>" +
                                    "<option value='option'>option text</option>" +
                                "</optgroup>" +
                            "</select>" +
                            "<textarea id='textarea'>input text</textarea>" +
                        "</form>" +
                        "<h1 id='h1'>heading 1</h1>" +
                        "<h2 id='h2'>heading 2</h2>" +
                        "<h3 id='h3'>heading 3</h3>" +
                        "<h4 id='h4'>heading 4</h4>" +
                        "<h5 id='h5'>heading 5</h5>" +
                        "<h6 id='h6'>heading 6</h6>" +
                        "<hr id='hr' />" +
                        "<iframe id='iframe' />" +
                        "<ol id='ol'>" +
                            "<li id='li'>list item text</li>" +
                        "</ol>" +
                        "<p id='p'>" +
                            "<a id='a'>anchor text</a>" +
                            "<abbr id='abbr'>abbreviation text</abbr>" +
                            "<acronym id='acronym'>acronym text</acronym>" +
                            "<address id='address'>address text</address>" +
                            "<b id='b'>Some text</b>" +
                            "<big id='big'>big text</big>, " +
                            "<blockquote id='blockquote'>blockquote text</blockquote>, " +
                            "<br id='br' />" +
                            "<cite id='cite'>citation text</cite>" +
                            "<code id='code'>code text</code>" +
                            "<del id='del'>deleted text</del>, " +
                            "<dfn id='dfn'>definition text</dfn>, " +
                            "<i id='i'>italic text</i>" +
                            "<img id='img' />" +
                            "<ins id='ins'>inserted text</ins>, " +
                            "<kbd id='kbd'>keyboard text</kbd>, " +
                            "<pre id='pre'>preformatted text</pre>, " +
                            "<q id='q'>quotation text</q>, " +
                            "<samp id='samp'>sample program output text</samp>, " +
                            "<small id='small'>small text</small>, " +
                            "<span id='span'>span text</span>, " +
                            "<sub id='sub'>subscript text</sub>, " +
                            "<sup id='sup'>superscript text</sup>, " +
                            "<tt id='tt'>teletype text</tt>" +
                            "<var id='var'>program variable text</var>" +
                        "</p>" +
                        "<table id='table'>" +
                            "<caption id='caption'>table caption text</caption>" +
                            "<colgroup id='colgroup' />" +
                            "<col id='col' />" +
                            "<thead id='thead'>" +
                                "<tr>" +
                                    "<th id='th'>table header text</th>" +
                                "</tr>" +
                            "</thead>" +
                            "<tbody id='tbody'>" +
                                "<tr id='tr'>" +
                                    "<td id='td'>table data text</td>" +
                                "</tr>" +
                            "</tbody>" +
                            "<tfoot id='tfoot'>" +
                                "<tr>" +
                                    "<td>table data text</td>" +
                                "</tr>" +
                            "</tfoot>" +
                        "</table>" +
                        "<ul id='ul'>" +
                        "</ul>" +
                    "</body>" +
                "</elements>").Deserialize<ElementCollection>();

            Body body = actual[0] as Body;

            Assert.NotNull(body);
            Assert.NotNull(body["a"] as Anchor);
            Assert.NotNull(body["abbr"] as Abbreviation);
            Assert.NotNull(body["acronym"] as Acronym);
            Assert.NotNull(body["address"] as Address);
            Assert.NotNull(body["b"] as Bold);
            Assert.NotNull(body["big"] as Big);
            Assert.NotNull(body["blockquote"] as Blockquote);
            Assert.NotNull(body["br"] as LineBreak);
            Assert.NotNull(body["button"] as PushButton);
            Assert.NotNull(body["button-reset"] as ResetButton);
            Assert.NotNull(body["button-submit"] as SubmitButton);
            Assert.NotNull(body["caption"] as Caption);
            Assert.NotNull(body["cite"] as Citation);
            Assert.NotNull(body["code"] as Code);
            Assert.NotNull(body["colgroup"] as ColumnGroup);
            Assert.NotNull(body["col"] as Column);
            Assert.NotNull(body["dd"] as DefinitionDescription);
            Assert.NotNull(body["del"] as Deletion);
            Assert.NotNull(body["dfn"] as Definition);
            Assert.NotNull(body["dl"] as DefinitionList);
            Assert.NotNull(body["dt"] as DefinitionTerm);
            Assert.NotNull(body["div"] as Division);
            Assert.NotNull(body["fieldset"] as Fieldset);
            Assert.NotNull(body["form"] as Form);
            Assert.NotNull(body["h1"] as Heading1);
            Assert.NotNull(body["h2"] as Heading2);
            Assert.NotNull(body["h3"] as Heading3);
            Assert.NotNull(body["h4"] as Heading4);
            Assert.NotNull(body["h5"] as Heading5);
            Assert.NotNull(body["h6"] as Heading6);
            Assert.NotNull(body["hr"] as HorizontalRule);
            Assert.NotNull(body["i"] as Italic);
            Assert.NotNull(body["iframe"] as InlineFrame);
            Assert.NotNull(body["img"] as Image);
            Assert.NotNull(body["input-button"] as ButtonInput);
            Assert.NotNull(body["input-checkbox"] as CheckBoxInput);
            Assert.NotNull(body["input-file"] as FileInput);
            Assert.NotNull(body["input-hidden"] as HiddenInput);
            Assert.NotNull(body["input-image"] as ImageInput);
            Assert.NotNull(body["input-password"] as PasswordInput);
            Assert.NotNull(body["input-radio"] as RadioInput);
            Assert.NotNull(body["input-reset"] as ResetInput);
            Assert.NotNull(body["input-submit"] as SubmitInput);
            Assert.NotNull(body["input-text"] as TextInput);
            Assert.NotNull(body["ins"] as Insertion);
            Assert.NotNull(body["kbd"] as Keyboard);
            Assert.NotNull(body["label"] as Label);
            Assert.NotNull(body["legend"] as Legend);
            Assert.NotNull(body["li"] as ListItem);
            Assert.NotNull(body["ol"] as OrderedList);
            Assert.NotNull(body["p"] as Paragraph);
            Assert.NotNull(body["pre"] as PreformattedText);
            Assert.NotNull(body["q"] as Quotation);
            Assert.NotNull(body["samp"] as Sample);
            Assert.True((body["select"] as Select).Contains("option"));
            Assert.NotNull(body["small"] as Small);
            Assert.NotNull(body["span"] as Span);
            Assert.NotNull(body["sub"] as Subscript);
            Assert.NotNull(body["sup"] as Superscript);
            Assert.NotNull(body["table"] as Table);
            Assert.NotNull(body["tbody"] as TableBody);
            Assert.NotNull(body["td"] as TableDataCell);
            Assert.NotNull(body["tfoot"] as TableFoot);
            Assert.NotNull(body["th"] as TableHeaderCell);
            Assert.NotNull(body["thead"] as TableHead);
            Assert.NotNull(body["tr"] as TableRow);
            Assert.NotNull(body["textarea"] as TextArea);
            Assert.NotNull(body["tt"] as Teletype);
            Assert.NotNull(body["ul"] as UnorderedList);
            Assert.NotNull(body["var"] as Variable);
        }

        [Fact]
        public void deserializeEmpty()
        {
            ElementCollection actual = "<elements />".Deserialize<ElementCollection>();

            Assert.Empty(actual);
        }

        [Fact]
        public void serialize()
        {
            ElementCollection obj = new ElementCollection();
            obj.Add(new Division() { Id = "abc" });
            obj.Add(new Division() { Id = "xyz" });

            XPathNavigator navigator = obj.Serialize().CreateNavigator();

            Assert.True((bool)navigator.Evaluate("1=count(/elements/div[@id='abc'])"));
            Assert.True((bool)navigator.Evaluate("1=count(/elements/div[@id='xyz'])"));
        }

        [Fact]
        public void ctor()
        {
            Assert.NotNull(new ElementCollection());
        }

        [Fact]
        public void prop_Count_get()
        {
            ElementCollection obj = new ElementCollection();
            obj.Add(new IElementDummy());

            Assert.Equal<int>(1, obj.Count);
        }

        [Fact]
        public void prop_Html()
        {
            Assert.True(new PropertyExpectations(typeof(ElementCollection).GetProperty("Html"))
                .DefaultValueIsNull()
                .TypeIs<string>()
                .Set(null)
                .Set(string.Empty)
                .Set("Contains <span>text</span>")
                .Result);
        }

        [Fact]
        public void prop_Html_get()
        {
            Assert.Null(new ElementCollection().Html);
        }

        [Fact]
        public void prop_Html_set()
        {
            string expected = "<h1>Title<h1>";

            ElementCollection obj = new ElementCollection();
            obj.Html = expected;

            string actual = obj.Html;

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void prop_IsReadOnly()
        {
            Assert.True(new PropertyExpectations(typeof(ElementCollection).GetProperty("IsReadOnly"))
                .DefaultValueIs(false)
                .TypeIs<bool>()
                .Result);
        }

        [Fact]
        public void indexer_int_get()
        {
            ElementCollection obj = (
                "<elements>" +
                    "<div id='abc' class='first'></div>" +
                    "<div id='xyz' class='last' />" +
                "</elements>").Deserialize<ElementCollection>();

            Assert.Equal<string>("first", obj[0].ToElement().Class);
            Assert.Equal<string>("last", obj[1].ToElement().Class);
        }

        [Fact]
        public void indexer_string_get()
        {
            ElementCollection obj = (
                "<elements>" +
                    "<div id='abc' class='first'></div>" +
                    "<div id='xyz' class='last' />" +
                "</elements>").Deserialize<ElementCollection>();

            Assert.Equal<string>("first", obj["abc"].Class);
            Assert.Equal<string>("last", obj["xyz"].Class);
        }

        [Fact]
        public void op_Add_IElement()
        {
            IElement expected = new IElementDummy();

            ElementCollection obj = new ElementCollection();
            obj.Html = "<h1>Title<h1>";
            obj.Add(expected);

            IElement actual = obj[0].ToElement();

            Assert.Same(expected, actual);
            Assert.Null(obj.Html);
        }

        [Fact]
        public void op_Clear()
        {
            ElementCollection obj = new ElementCollection();
            obj.Add(new IElementDummy());
            obj.Clear();

            Assert.Null(obj.Html);
            Assert.Equal<int>(0, obj.Count);
        }

        [Fact]
        public void op_Clear_html()
        {
            ElementCollection obj = new ElementCollection();
            obj.Html = "<h1>Title<h1>";
            obj.Clear();

            Assert.Null(obj.Html);
            Assert.Equal<int>(0, obj.Count);
        }

        [Fact]
        public void op_Contains_IElement()
        {
            IElement item = new IElementDummy();

            ElementCollection obj = new ElementCollection();

            Assert.False(obj.Contains(item));

            obj.Add(item);

            Assert.True(obj.Contains(item));
        }

        [Fact]
        public void op_CopyTo_IElementArray_int()
        {
            IElement expected = new IElementDummy();

            ElementCollection obj = new ElementCollection();
            obj.Add(expected);

            IElement[] array = new IElement[1];

            obj.CopyTo(array, 0);

            IElement actual = array[0];

            Assert.Same(expected, actual);
        }

        [Fact]
        public void IEnumerable_op_GetEnumerator()
        {
            Assert.NotNull((new ElementCollection() as IEnumerable).GetEnumerator());
        }

        [Fact]
        public void IEnumerableOfT_op_GetEnumerator()
        {
            Assert.NotNull(new ElementCollection().GetEnumerator());
        }

        [Fact]
        public void op_IndexOf_IElement()
        {
            IElement item = new IElementDummy();

            ElementCollection obj = new ElementCollection();

            Assert.Equal<int>(-1, obj.IndexOf(item));

            obj.Add(item);

            Assert.Equal<int>(0, obj.IndexOf(item));
        }

        [Fact]
        public void op_Insert_int_IElement()
        {
            ElementCollection obj = new ElementCollection();
            obj.Add(new IElementDummy());

            IElement expected = new IElementDummy();

            obj.Insert(0, expected);

            IElement actual = obj[0].ToElement();

            Assert.Same(expected, actual);
        }

        [Fact]
        public void op_ReadXml_XmlReader()
        {
            ElementCollection obj = new ElementCollection();

            using (StringReader xml = new StringReader("<div>text<!-- comment --></div>"))
            {
                using (XmlReader reader = XmlReader.Create(xml))
                {
                    obj.ReadXml(reader);
                }
            }

            Assert.NotNull(obj[0] as Division);
        }

        [Fact]
        public void op_ReadXml_XmlReaderNull()
        {
            Assert.Throws<ArgumentNullException>(() => new ElementCollection().ReadXml(null as XmlReader));
        }

        [Fact]
        public void op_Remove_IElement()
        {
            IElement item = new IElementDummy();

            ElementCollection obj = new ElementCollection();
            obj.Add(item);

            Assert.True(obj.Contains(item));

            obj.Remove(item);

            Assert.False(obj.Contains(item));
        }

        [Fact]
        public void op_RemoveAt_int()
        {
            IElement item = new IElementDummy();

            ElementCollection obj = new ElementCollection();
            obj.Add(item);

            Assert.True(obj.Contains(item));

            obj.RemoveAt(0);

            Assert.False(obj.Contains(item));
        }

        [Fact]
        public void op_Render()
        {
            ElementCollection obj = new ElementCollection();
            obj.Add(new Division());

            string expected = "<div></div>";
            string actual = obj.Render();

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void op_ToString()
        {
            ElementCollection obj = new ElementCollection();
            obj.Add(new Division());

            string expected = obj.Render();
            string actual = obj.ToString();

            Assert.Equal<string>(expected, actual);
        }

        [Fact]
        public void IXmlSerializable_op_GetSchema()
        {
            Assert.Throws<NotSupportedException>(() => (new ElementCollection() as IXmlSerializable).GetSchema());
        }

        [Fact]
        public void IXmlSerializable_op_ReadXml_XmlReaderNull()
        {
            Assert.Throws<ArgumentNullException>(() => (new ElementCollection() as IXmlSerializable).ReadXml(null as XmlReader));
        }

        [Fact]
        public void IXmlSerializable_op_WriteXml_XmlWriterNull()
        {
            Assert.Throws<ArgumentNullException>(() => (new ElementCollection() as IXmlSerializable).WriteXml(null as XmlWriter));
        }
    }
}